
package com.parking.status.sync.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class TCPServer {

//    @Value("${tcp.ip:127.0.0.1}")
    private String tcpIp;

//    @Value("${tcp.port:20000}")
    private int tcpPort;

//    @Value("${boss.thread.count:2}")
    private int bossCount;

//    @Value("${worker.thread.count:2}")
    private int workerCount;

//    @Value("${so.keepalive:true}")
    private boolean keepAlive;

//    @Value("${so.backlog:100}")
    private int backlog;

    private ApplicationContext applicationContext;

    private ServerChannelInitializer serverChannelInitializer;

    private ServerBootstrap serverBootstrap;

    private InetSocketAddress tcpAddr;

    private ChannelRepository channelRepository;

    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workerGroup;

    private Channel serverChannel;

    public Channel getServerChannel() {
        return serverChannel;
    }

    public void setServerChannel(Channel serverChannel) {
        this.serverChannel = serverChannel;
    }

    public TCPServer(ApplicationContext applicationContext, int port, int bosscount, int workercount, boolean keepalive, int backlog) {
        this.applicationContext = applicationContext;
        this.tcpPort = port;
        this.bossCount = bosscount;
        this.workerCount = workercount;
        this.keepAlive = keepalive;
        this.backlog = backlog;
        init();
    }

    private void init() {

        serverChannelInitializer = (ServerChannelInitializer)applicationContext.getBean("serverChannelInitializer");
        bossGroup = new NioEventLoopGroup(bossCount);
        workerGroup = new NioEventLoopGroup(workerCount);
        tcpAddr = new InetSocketAddress(tcpPort);
        channelRepository = new ChannelRepository();
        this.serverBootstrap = bootstrap();
    }

    @SuppressWarnings("unchecked")
    public ServerBootstrap bootstrap() {
        serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(serverChannelInitializer);
        Map<ChannelOption<?>, Object> tcpChannelOptions = tcpChannelOptions();
        Set<ChannelOption<?>> keySet = tcpChannelOptions.keySet();
        for (@SuppressWarnings("rawtypes") ChannelOption option : keySet) {
            serverBootstrap.option(option, tcpChannelOptions.get(option));
        }
        return serverBootstrap;
    }

    public Map<ChannelOption<?>, Object> tcpChannelOptions() {
        Map<ChannelOption<?>, Object> options = new HashMap<ChannelOption<?>, Object>();
        options.put(ChannelOption.SO_KEEPALIVE, keepAlive);
        options.put(ChannelOption.SO_BACKLOG, backlog);
        return options;
    }

    //@PostConstruct
    public void start() throws Exception {
        try {
            serverChannel = serverBootstrap.bind(tcpAddr).sync().channel().closeFuture().sync().channel();
        } finally {
            bossGroup.shutdownGracefully().sync();
            workerGroup.shutdownGracefully().sync();
        }
    }

    //@PreDestroy
    public void stop() throws Exception {
        serverChannel.close();
        serverChannel.parent().close();

    }

}
